package demo.tcpClient;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.CombinedChannelDuplexHandler;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.TypeParameterMatcher;

public abstract class SimpleChannelDuplexHandler<I> extends CombinedChannelDuplexHandler<ChannelInboundHandler, ChannelOutboundHandler> {

	
	private final TypeParameterMatcher matcher;
	private final boolean autoRelease;
	public SimpleChannelDuplexHandler() {
		this(true);
	}

	/**
	 * Create a new instance which will try to detect the types to match out of
	 * the type parameter of the class.
	 * 
	 * @param autoRelease
	 *            {@code true} if handled messages should be released
	 *            automatically by pass them to
	 *            {@link ReferenceCountUtil#release(Object)}.
	 */
	protected SimpleChannelDuplexHandler(boolean autoRelease) {
		matcher = TypeParameterMatcher.find(this,
				SimpleChannelDuplexHandler.class, "I");
		this.autoRelease = autoRelease;
	}

	/**
	 * @see {@link #SimpleChannelInboundHandler(Class, boolean)} with
	 *      {@code true} as boolean value.
	 */
	protected SimpleChannelDuplexHandler(Class<? extends I> inboundMessageType) {
		this(inboundMessageType, true);
	}

	/**
	 * Create a new instance
	 * 
	 * @param inboundMessageType
	 *            The type of messages to match
	 * @param autoRelease
	 *            {@code true} if handled messages should be released
	 *            automatically by pass them to
	 *            {@link ReferenceCountUtil#release(Object)}.
	 */
	protected SimpleChannelDuplexHandler(Class<? extends I> inboundMessageType,
			boolean autoRelease) {
		matcher = TypeParameterMatcher.get(inboundMessageType);
		this.autoRelease = autoRelease;
	}

	/**
	 * Returns {@code true} if the given message should be handled. If
	 * {@code false} it will be passed to the next {@link ChannelInboundHandler}
	 * in the {@link ChannelPipeline}.
	 */
	public boolean acceptInboundMessage(Object msg) throws Exception {
		return matcher.match(msg);
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {
		boolean release = true;
		try {
			if (acceptInboundMessage(msg)) {
				@SuppressWarnings("unchecked")
				I imsg = (I) msg;
				channelRead0(ctx, imsg);
			} else {
				release = false;
				ctx.fireChannelRead(msg);
			}
		} finally {
			if (autoRelease && release) {
				ReferenceCountUtil.release(msg);
			}
		}
	}

	protected abstract void channelRead0(ChannelHandlerContext ctx, I msg)throws Exception;
}
